﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MipsMachine
{
	
	public class MipsProject
	{
		MipsDirectory parent_dir;
		string filename;
		MipsProgram built_program;
		DateTime last_build;
		private string output_filename;
		public MipsDirectory Directory { get { return parent_dir; } }
		public string Filename { get { return filename; } }

		public MipsProgram Program { get { return built_program; } set { built_program = value; } }

		public string Output { get { return output_filename; } set { output_filename = value; } }

		public void Save()
		{
			foreach (var file in parent_dir.GetFiles())
				file.Save();
		}
		/// <summary>
		/// Compiles the entire project if needed.
		/// </summary>
		/// <param name="progress"></param>
		/// <param name="callback"></param>
		public void Compile(Action<string, int> progress, Action<CompilerLogEntry> callback)
		{
			if (!IsUpToDate())
			{
				
				MipsProgram prog = null;
				MipsAssembler asm = new MipsAssembler();
				asm.SearchPaths.Add(parent_dir.Name);
				
				var files = from item in parent_dir.GetFiles() select item.GetFilePair();
				foreach (var file in parent_dir.GetFiles())
					file.Archive = false;
				asm.Assemble(files, progress, out prog, callback);
				if (prog != null)
				{
					built_program = prog;
					string path = System.IO.Path.Combine(parent_dir.Name, output_filename);
					if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(path)))
						System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
					prog.SaveBin(System.IO.Path.Combine(parent_dir.Name,output_filename));
				}
				last_build = DateTime.Now;
			}
			
		}
		/// <summary>
		/// Retrieves if build is up to date
		/// </summary>
		/// <returns></returns>
		public bool IsUpToDate()
		{
			if (built_program == null)
				return false;
			else
			{
				var ie = from item in parent_dir.GetFiles() where item.Archive select item;
				return ie.Count() == 0;
			}
		}
		public MipsProject(string filename)
		{
			this.filename = System.IO.Path.GetFullPath(filename);
			parent_dir = new MipsDirectory(System.IO.Path.GetDirectoryName(this.filename));
			output_filename = "bin\\output.bin";
		}
	}
	/// <summary>
	/// Base class for project tree
	/// </summary>
	public abstract class MipsEntry
	{
		private string name;
		private MipsDirectory parent;
		private List<MipsEntry> children;
		public System.Collections.ObjectModel.ReadOnlyCollection<MipsEntry> Children { get { return children.AsReadOnly(); }}

		

		protected MipsEntry(string filename)
		{
			name = filename;
			children = new List<MipsEntry>();
		}
		/// <summary>
		/// Item filename
		/// </summary>
		public string Name 
		{ 
			get 
			{ 
				return name; 
			} 
			set
			{
				if (System.IO.File.Exists(name))
				{
					System.IO.File.Replace(name, value, name + ".backup");
				}
				name = value;
			} 
		}
		/// <summary>
		/// Parent directory
		/// </summary>
		public MipsDirectory Parent
		{
			get
			{
				return parent;
			}
			set
			{
				if (parent != null)
					parent.children.Remove(this);
				if (value != null)
					value.children.Add(this);
				parent = value;
			}
		}
		public override string ToString()
		{
			return System.IO.Path.GetFileName(name);
		}
	}
	/// <summary>
	/// Represents a code file in the project
	/// </summary>
	public class MipsSourceFile : MipsEntry
	{
		private string code;
		private bool archive;
		public string Code { get { return code; } set { code = value; archive = true; } }

		public MipsAssembler.FilePair GetFilePair()
		{
			return new MipsAssembler.FilePair(Name, code);
		}
		public bool Archive
		{
			get
			{
				if (System.IO.File.Exists(Name))
					return (System.IO.File.GetAttributes(Name) & System.IO.FileAttributes.Archive) == System.IO.FileAttributes.Archive;
				else
					return archive;
			}
			set
			{
				if (System.IO.File.Exists(Name))
				{
					var attr = System.IO.File.GetAttributes(Name);
					attr = attr & ~System.IO.FileAttributes.Archive;
					if (value)
						attr |= System.IO.FileAttributes.Archive;
				}
				archive = value;
			}
		}
		public DateTime LastChange
		{
			get
			{
				return System.IO.File.GetLastWriteTime(Name);
			}
		}
		public void Save()
		{
			System.IO.File.WriteAllText(Name, code);
			Archive = true;
		}
		public void Reload()
		{
			if(System.IO.File.Exists(Name))
				code = System.IO.File.ReadAllText(Name);
		}
		public MipsSourceFile(string filename)
			: base(filename)
		{
			Reload();
		}
	}
	/// <summary>
	/// Represents a directory in a project
	/// </summary>
	public class MipsDirectory : MipsEntry
	{
		public IEnumerable<MipsSourceFile> GetFiles()
		{
			foreach (var item in Children)
			{
				if (item is MipsDirectory)
				{
					foreach (var i in (item as MipsDirectory).GetFiles())
						yield return i;
				}
				else
					yield return item as MipsSourceFile;
			}
		}
		public MipsSourceFile AddNewFile(string name)
		{
			var ret = new MipsSourceFile(System.IO.Path.Combine(Name, name));
			ret.Parent = this;
			return ret;
		}
		public MipsDirectory(string name)
			: base(name)
		{
		}
	}
}
